/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package tds;

import com.google.gson.*;
import com.google.gson.reflect.TypeToken;
import java.util.*;
import java.io.*;
import java.net.*;
import java.lang.reflect.Type;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import javax.swing.JOptionPane;

/**
 *
 * @author cwhitela
 */
public class Twitter {
	
	public enum SearchResultType {mixed, recent, popular}
	private DateFormat df;
	private MyDateFormatter mdf;
	final public static String twitterDateFormatString = "yyyy-MM-dd";
	final private static int RESTRateLimitCode = 400;
	final private static int searchRateLimitCode = 420;
	
	public static class ErrorMessages {
		
		final public static String HTTP404 = "Twitter returned HTTP 404 error.  This username may not exist.";
	}
	
	public Twitter()
	{
		df = new SimpleDateFormat(twitterDateFormatString);
		mdf = new MyDateFormatter();
	}

	public void writeTweets(BufferedWriter writer, Collection<Tweet> tweets) throws IOException {
		
		for (Tweet tweet : tweets)
		{
			writer.write(tweet.toString());
			writer.newLine();
		}

		writer.flush();
	}
	
	public void writeMentions (BufferedWriter writer, Collection<String> mentions) throws IOException
	{
		if (mentions != null)
		{
			for (String mention : mentions)
			{
				writer.write(mention);
				writer.newLine();
			}
		}
		else
		{
			JOptionPane.showMessageDialog(null, "Mentions is null");
		}
		
		writer.flush();
	}
	
	// Retrieves full userTimeline
	public UserTimeline getUserTimeline(final String username) throws ProtectedUserException, RateLimitException, NoSuchUserException, IOException {
		
		return getUserTimeline(username, null, null);
	}
	
	// Returns full UserTimeline object specified by a formatted date regex
	public UserTimeline getUserTimeline(final String username, final Date from, final Date to) throws ProtectedUserException, RateLimitException, NoSuchUserException, IOException {

		Collection<Tweet> tweets = new ArrayList<Tweet>();

		int retryCount = 0;
		for (int i = 1; i <= 20; ++i) {

			Collection<Tweet> currentTimeline = new ArrayList<Tweet>();

			try
			{
				currentTimeline = getUserTimelinePage(username, i);
				
				// below: no exceptions
				
				retryCount = 0;
				if (currentTimeline.isEmpty())
				{
					break;
				}
				else
				{
					if (from != null && to != null)
					{
						for (Tweet tweet : currentTimeline)
						{
							try
							{
								Date date = df.parse(mdf.getFormattedDateString(tweet));
								if (date.after(from) && date.before(to))
									tweets.add(tweet);
							}
							catch (ParseException pe)
							{
								System.out.println(pe.getMessage());
							}
							
						}
					}
					else
					{
						tweets.addAll(currentTimeline);
					}
				}
			}
			catch (JsonSyntaxException e)
			{
				try {
					Thread.sleep(2000);
				} catch (InterruptedException ie) {}
				// Repeat this iteration of loop
				--i;
				continue;
			}
			catch (IOException e)
			{
				if (e.getMessage().contains("404"))
				{
					if (i == 1)
					{
						throw new NoSuchUserException("User " + username + " doesn't appear to exist");
					}
					else
					{
						if (retryCount++ > 1)
							throw e;
					}
				}
				try {
					Thread.sleep(2000);
				} catch (InterruptedException ie) {}
				// Repeat this iteration of loop
				--i;
				continue;
			}
		}

		return new UserTimeline(username, tweets);
	}
	
	public UserTimeline getUserTimeline2(UserTimelineQuery utq)
	{
		throw new UnsupportedOperationException();
		//return new UserTimeline(username, tweets);
	}
	
	// Only returns a collection of tweets.  May change this to a UserTimeline object.
	public Collection<Tweet> getUserTimelinePage(final String username, final int page) throws ProtectedUserException, RateLimitException, IOException, JsonSyntaxException {

		Collection<Tweet> tweets = new ArrayList<Tweet>();
		final String address = "http://api.twitter.com/1/statuses/user_timeline.json?count=200&page=" + page + "&screen_name=" + username + "&include_entities=true";
		URL url = new URL(address);
		HttpURLConnection connection = (HttpURLConnection) url.openConnection();
		
		// Handle protected user in advance
		if (connection.getResponseCode() == HttpURLConnection.HTTP_UNAUTHORIZED)
		{
			throw new ProtectedUserException("User " + username + " appears to be protected.");
		}
		// Stop if being rate limited
		else if (connection.getResponseCode() == Twitter.RESTRateLimitCode)
		{
			throw new RateLimitException();
		}
		// Continue if no errors
		else
		{
			Reader reader = new InputStreamReader(connection.getInputStream());

			Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
			Collection<UserTimelineResponse.Entry> entries = new ArrayList<UserTimelineResponse.Entry>();

			Type typeOfT = new TypeToken<Collection<UserTimelineResponse.Entry>>(){}.getType();

			entries = gson.fromJson(reader, typeOfT);

			for (UserTimelineResponse.Entry curEntry : entries) {
				tweets.add(new Tweet(curEntry));
			}

			// Purposefully handling this exception here
			try {
				if (reader != null)
					reader.close();
			} catch (IOException ex) {
				System.out.println(ex.getMessage());
			}
		}

		return tweets;

	}
	
	// pulls based off tweet entities
	public Collection<String> getMentionsAndRetweets(Collection<Tweet> tweets)
	{
		Collection<String> mentions = new ArrayList();
		
		for (Tweet tweet : tweets)
		{
			if (tweet != null)
			{
				if (tweet.mentions != null)
				{
					for (String mention : tweet.mentions)
					{
						if ( !tweet.mentions.contains(mention) )
							mentions.add(mention);
					}
				}
			}
			
		}
		
		return mentions;
	}
	
	public void writeMentions(Collection<String> mentions, String saveLocation)
	{
		BufferedWriter writer = null;
				
		try
		{
			writer = new BufferedWriter(new FileWriter (new File (saveLocation + File.separatorChar + "mentions.txt")));
			
			for (String mention : mentions)
			{
				if (mention != null)
				{
					writer.write(mention + "\n");
					writer.flush();
				}
			}
		}
		catch (IOException e)
		{
			JOptionPane.showMessageDialog(null, "IOException occurred.");
		}
		finally
		{
			try
			{
				if (writer != null)
					writer.close();
			}
			catch (IOException e)
			{
				// Not sure whether there can be anything done here except to notify the user
				// But for the moment I don't want a message box to appear because if this is being automated it would interrupt
				// the rest of the procedure until the user clicks "OK".
			}
		}
	}

	public SearchResult search(SearchQuery query) throws SearchRateLimitException, TwitterException, MalformedURLException, IOException
	{
		Collection<Tweet> tweets = new ArrayList<Tweet>();
		String maxID = "0";
		
		while (query.hasMorePages()) {
			
			query.nextPage();
			String qStr = query.toString();
			HttpURLConnection connection = (HttpURLConnection) new URL(qStr).openConnection();
			switch (connection.getResponseCode())
			{
				case Twitter.searchRateLimitCode: throw new SearchRateLimitException("Rate Limit Exceeded");
				case HttpURLConnection.HTTP_UNAUTHORIZED: throw new TwitterException("Search error with since and/or until parameters");
			}

			Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
			Type typeOfT = new TypeToken<SearchResponse>(){}.getType();
			SearchResponse response = gson.fromJson(new InputStreamReader(connection.getInputStream()), typeOfT);
			if ( response.getResults().isEmpty() )
					System.out.println("Empty");
			else {
				tweets.addAll(Tweet.massConstructor(response.getResults()));
				if (response.getMax_id_str().compareTo(maxID) > 0)
					maxID = response.getMax_id_str();
			}
			
		}
		
		return new SearchResult(query.query, tweets, maxID);
	}
        
	public RateLimitStatus getRateLimit() throws IOException, JsonSyntaxException {

		// Variables
		final Gson gson = new GsonBuilder().create();
		final String address = "http://api.twitter.com/1/account/rate_limit_status.json";
		final Reader reader = new BufferedReader(new InputStreamReader(new URL(address).openStream()));
		//Type typeOfT = new TypeToken<RateLimitResponse>(){}.getType();
		final RateLimitResponse response = gson.fromJson(reader, RateLimitResponse.class);		
		
		try {
			reader.close();
		} catch (IOException ex) {
			System.out.println(ex.getMessage());
		}

		return new RateLimitStatus(response);

	}
}
